{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "J1w-zrxAUr4l",
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "# Quick Start Tutorial of Scenario Simulation\n",
    "\n",
    "Welcome to try out MetaDrive & ScenarioNet!\n",
    "\n",
    "The simulation supports two running modes:\n",
    "\n",
    "1. **With 3D rendering functionality**: MetaDrive can easily install and run in personal computer, but may need special treatments for 3D rendering in headless machine and cloud servers.\n",
    "\n",
    "2. **Without 3D rendering functionality**: MetaDrive can easily install and run in any machine. In this Colab notebook, we demonstrate MetaDrive in this mode and the renderer will be the **2D** **Pygame** renderer.\n",
    "\n",
    "In this tutorial, we will navigate you through the installation and some basic functionality of the simulator!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2efvTXdHVptN",
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Installation\n",
    "\n",
    "You can install MetaDrive easily."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000
    },
    "id": "65J2iQKpUQ1B",
    "outputId": "60411b22-c699-4db0-bf23-13b4c83c4d4f",
    "pycharm": {
     "name": "#%%\n"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "#@title Collect the MetaDrive & ScenarioNet\n",
    "# NOTE: If you are running this notebook locally with installtion finished, this step is not required.\n",
    "RunningInCOLAB = 'google.colab' in str(get_ipython()) # Detect if it is running in Colab\n",
    "if RunningInCOLAB:\n",
    "    %pip install git+https://github.com/metadriverse/metadrive.git\n",
    "    %pip install git+https://github.com/metadriverse/scenarionet.git"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, let's create a 2D visualization tool for recording the scenario in GIF."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# visualization\n",
    "from IPython.display import Image as IImage\n",
    "import pygame\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "\n",
    "def make_GIF(frames, name=\"demo.gif\"):\n",
    "    print(\"Generate gif...\")\n",
    "    imgs = [frame for frame in frames]\n",
    "    imgs = [Image.fromarray(img) for img in imgs]\n",
    "    imgs[0].save(name, save_all=True, append_images=imgs[1:], duration=50, loop=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuration\n",
    "\n",
    "Let's import some modules and specify the dataset directory.\n",
    "**Note: if your machine supports 3D OpenGL rendering, you can turn on the *threeD_render* flag in the following cell. It will render both the 2D results and 3D results.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Make some configurations and import some modules\n",
    "from metadrive.engine.engine_utils import close_engine\n",
    "close_engine()\n",
    "from metadrive.pull_asset import pull_asset\n",
    "pull_asset(False)\n",
    "# NOTE: usually you don't need the above lines. It is only for avoiding a potential bug when running on colab\n",
    "\n",
    "from metadrive.engine.asset_loader import AssetLoader\n",
    "from metadrive.policy.replay_policy import ReplayEgoCarPolicy\n",
    "from metadrive.envs.scenario_env import ScenarioEnv\n",
    "import os\n",
    "\n",
    "threeD_render=False # turn on this to enable 3D render. It only works when you have a screen and not running on Colab.\n",
    "threeD_render=threeD_render and not RunningInCOLAB\n",
    "os.environ[\"SDL_VIDEODRIVER\"] = \"dummy\" # Hide the pygame window\n",
    "waymo_data =  AssetLoader.file_path(AssetLoader.asset_path, \"waymo\", unix_style=False) # Use the built-in datasets with simulator\n",
    "nuscenes_data =  AssetLoader.file_path(AssetLoader.asset_path, \"nuscenes\", unix_style=False) # Use the built-in datasets with simulator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.listdir(waymo_data) # there are 3 waymo scenario file with a 'dataset_summary.pkl'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.listdir(nuscenes_data) # there are 10 nuscenes scenario file with a 'dataset_summary.pkl' and a 'dataset_summary.pkl'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simulate one Waymo scenario\n",
    "The simulation interface is in gym-style and let's create a environment first. \n",
    "By specifying the *data_directory*, we can load the Waymo dataset to simulation. *num_scenarios* is used to determine how many scenarios are loaded from the datasets. Here we only load one scenario from the Waymo dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = ScenarioEnv(\n",
    "    {\n",
    "        \"manual_control\": False,\n",
    "        \"reactive_traffic\": False,\n",
    "        \"use_render\": threeD_render,\n",
    "        \"agent_policy\": ReplayEgoCarPolicy,\n",
    "        \"data_directory\": waymo_data,\n",
    "        \"num_scenarios\": 1\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the simulation can run with *env.step()* and *env.reset(seed=scenario-index)*. Their functions are as follows.\n",
    "\n",
    "- The *env.reset(seed=scenario-index)* tells the simulator to remove all existing objects created in last episode, create a new scenario with index *scenario-index* and start a new episode.\n",
    "**As we only have one scenario loaded to the simulator, the *scenario-index* can only be 1 in this example.**\n",
    "\n",
    "\n",
    "- *env.step()* will progress the simulation by one step (0.1 second) and return the new observation, reward and termination flag. It takes an action as input which is a 2-dim vector representing the throttle and steering angle for the ego car.\n",
    "As we are using the *ReplayEgoCarPolicy* here, the ego car will not take the external action accepted from *env.step()*.\n",
    "**Instead, the ego car will follow the recorded trajectory. Thus the following code is for playing one recorded scenario including maps and trajetcories.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @title Run Simulation\n",
    "\n",
    "o, _ = env.reset(seed=0)\n",
    "frames = []\n",
    "for i in range(1, 100000):\n",
    "    o, r, tm, tc, info = env.step([1.0, 0.])\n",
    "    frames.append(env.render(mode=\"top_down\",film_size=(1200, 1200)))\n",
    "    if tm or tc:\n",
    "        break\n",
    "env.close()\n",
    "\n",
    "make_GIF(frames)\n",
    "# visualization\n",
    "IImage(open(\"demo.gif\", 'rb').read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simulate Multiple nuScenes scenarios\n",
    "For simulating multiple scenarios, just modify the *num_scenarios* and use *env.reset(seed=target_index)* to select the scenario of interest.\n",
    "This example loading all scenarios into simulator but only simulate and visualize 2 of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = ScenarioEnv(\n",
    "    {\n",
    "        \"manual_control\": False,\n",
    "        \"reactive_traffic\": False,\n",
    "        \"use_render\": False,\n",
    "        \"agent_policy\": ReplayEgoCarPolicy,\n",
    "        \"data_directory\": nuscenes_data, # use nuscenes data\n",
    "        \"num_scenarios\": 10, # load 10 scenarios\n",
    "    }\n",
    ")\n",
    "\n",
    "for seed in range(2): # only simulate the first 2 scenarios\n",
    "    print(\"\\nSimulate Scenario: {}\".format(seed))\n",
    "    o, _ = env.reset(seed=seed)\n",
    "    frames = []\n",
    "    for i in range(1, 100000):\n",
    "        o, r, tm, tc, info = env.step([1.0, 0.])\n",
    "        frames.append(env.render(mode=\"top_down\",film_size=(4000, 4000), screen_size=(500, 500)))\n",
    "        if tm or tc:\n",
    "            make_GIF(frames, name=\"scenario_{}.gif\".format(seed))\n",
    "            break\n",
    "\n",
    "env.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install imageio\n",
    "\n",
    "import imageio\n",
    "import numpy as np    \n",
    "\n",
    "#Create reader object for the gif\n",
    "gif1 = imageio.get_reader('scenario_0.gif')\n",
    "gif2 = imageio.get_reader('scenario_1.gif')\n",
    "\n",
    "\n",
    "#If they don't have the same number of frame take the shorter\n",
    "number_of_frames = min(gif1.get_length(), gif2.get_length())-1\n",
    "\n",
    "#Create writer object\n",
    "new_gif = imageio.get_writer('output.gif')\n",
    "\n",
    "for frame_number in range(number_of_frames):\n",
    "    img1 = gif1.get_next_data()\n",
    "    img2 = gif2.get_next_data()\n",
    "    #here is the magic\n",
    "    new_image = np.hstack((img1, img2))\n",
    "    new_gif.append_data(new_image)\n",
    "\n",
    "gif1.close()\n",
    "gif2.close()    \n",
    "new_gif.close()\n",
    "\n",
    "# visulization\n",
    "IImage(open(\"output.gif\", 'rb').read())"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
